﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using SimpleIR;
using System.Diagnostics.Contracts;

namespace RgnAnalyzer.Domain
{
	/// <summary>
	/// Abstract class representing a memory location in the program. Possible types of memory locations are: 
	/// (1) Objects allocated on the heap.
	/// (2) Fixed storage locations for locals and globals.
	/// </summary>
	internal abstract class ALocation
	{
		protected readonly IRType m_loctype;

		/// <summary>
		/// The position of this location in the memory.
		/// </summary>
		protected int m_locationaddress;

		/// <summary>
		/// Generate identity tags.
		/// </summary>
		private static int s_idTagCtr = 1;

		/// <summary>
		/// Reset the identity generator.
		/// </summary>
		internal static void ResetIDTagCtrForAnalysis()
		{ s_idTagCtr = 1; }

		/// <summary>
		/// Generate a fresh id tag for moving into a call.
		/// </summary>
		internal static int GenNextIdentityTag()
		{ return s_idTagCtr++; }

		/// <summary>
		/// To reduce GC pressure we have a singleton represetation for the empty identity array.
		/// </summary>
		internal static int[] EmptyIdentity = new int[] { };

		/// <summary>
		/// The identities of for this location.
		/// </summary>
		protected int[] m_identities;

		/// <summary>
		/// To reduce GC pressure we have a singleton represetation for the empty allocation/escape site array.
		/// </summary>
		internal static EscapeSiteInfo[] EmptyAllocLines = new EscapeSiteInfo[] { };

		/// <summary>
		/// The orig line this appeared in this method (i.e. the alloc line or the line it escaped at).
		/// Not valid for fixed locations.
		/// </summary>
		protected EscapeSiteInfo[] m_escapelines;

		/// <summary>
		/// Array containing all info on positions/targets/etc. for any pointers stored in this location.
		/// </summary>
		protected APtrValueMap m_ptrinfo;

		/// <summary>
		/// The use/mod info for this location.
		/// </summary>
		protected List<UseModInfo> m_uml;

		[ContractInvariantMethod]
		private void ObjectInvariant()
		{
			Contract.Invariant(this.m_loctype != null);

			Contract.Invariant(this.m_identities != null);
			Contract.Invariant((this.m_identities.Length != 0) | (this.m_identities == EmptyIdentity));
			
			Contract.Invariant(this.m_escapelines != null);
			Contract.Invariant((this.m_escapelines.Length != 0) | (this.m_escapelines == EmptyAllocLines));

			Contract.Invariant((!(this.m_loctype is IRPtrType | this.m_loctype is IRClassType | this.m_loctype is IRStructType)) || (this.m_ptrinfo.PTargetCount != 0 | this.m_ptrinfo.PTargets == APtrValueMap.EmptyTargetMap));
			Contract.Invariant(this.m_uml != null);
		}

		internal ALocation(int address, int[] identity, EscapeSiteInfo[] esclocinfo, APtrValueMap app, List<UseModInfo> uml, IRType loctype)
		{
			Contract.Requires(loctype != null);

			Contract.Requires((identity != null) && (UniqueSortedArray.IsUniqueSortedArray<int>(identity)));
			Contract.Requires((identity.Length != 0) | (identity == EmptyIdentity));
			Contract.Requires(esclocinfo != null && (UniqueSortedArray.IsUniqueSortedArray<EscapeSiteInfo>(esclocinfo)));
			Contract.Requires(esclocinfo.Length != 0 | esclocinfo == EmptyAllocLines);
			Contract.Requires((!(loctype is IRPtrType | loctype is IRClassType | loctype is IRStructType)) || (app.PTargetCount != 0 | app.PTargets == APtrValueMap.EmptyTargetMap));
			Contract.Requires((identity != EmptyIdentity) | (esclocinfo != EmptyAllocLines));
			Contract.Requires(uml != null);

			this.m_loctype = loctype;
			this.m_identities = identity;
			this.m_escapelines = esclocinfo;
			this.m_locationaddress = address;
			this.m_ptrinfo = app;
			this.m_uml = uml;
		}

		/// <summary>
		/// Create an indentical deep copy of this location.
		/// </summary>
		internal abstract ALocation CopyLocation();

		/// <summary>
		/// Get the type of the location
		/// </summary>
		internal IRType LocationType
		{ get { return this.m_loctype; } }

		/// <summary>
		/// Get the address of the location
		/// </summary>
		internal int Address
		{ get { return this.m_locationaddress; } }

		/// <summary>
		/// True if the allocation info value indicates the objects/fixed location MAY BE either (1) allocated in the current call scope or (2) has escaped from a callee method.
		/// </summary>
		[Pure]
		internal bool MayBeInternalAlloc()
		{ return this.m_escapelines != ALocation.EmptyAllocLines; }

		/// <summary>
		/// True if the allocation info value indicates the objects/fixed location MAY BE passed in from the external scope.
		/// </summary>
		[Pure]
		internal bool MayBeExternalAlloc()
		{ return this.m_identities != ALocation.EmptyIdentity; }

		/// <summary>
		/// True if the allocation info value indicates the objects MUST BE either (1) allocated in the current call scope or (2) has escaped from a callee method.
		/// </summary>
		[Pure]
		internal bool MustBeInternalAlloc()
		{ return this.m_identities == EmptyIdentity; }

		/// <summary>
		/// True if this MUST represent objects or fixed memory locations that came from an external scope.
		/// </summary>
		[Pure]
		internal bool MustBeExternalAlloc()
		{ return this.m_escapelines == EmptyAllocLines; }

		internal string GetAllocLocationStr()
		{
			if(this.MustBeExternalAlloc())
				return "HE";
			else
			{
				StringBuilder sb = new StringBuilder("@[");
				for(int i = 0; i < this.m_escapelines.Length; ++i)
				{
					if(i != 0)
						sb.Append(", ");
					sb.Append(this.m_escapelines[i].ToString());
				}
				sb.Append("]");

				if(this.MayBeInternalAlloc())
					return "HI" + sb.ToString();
				else
					return "HIE" + sb.ToString();
			}
		}

		/// <summary>
		/// Return the escape line information for this location
		/// </summary>
		internal EscapeSiteInfo[] AllocationEscapeSites
		{ get { return this.m_escapelines; } }

		/// <summary>
		/// Return the use/mod info for this location
		/// </summary>
		internal List<UseModInfo> UMInfo
		{ get { return this.m_uml; } }

		/// <summary>
		/// Get the identities associated with the location
		/// </summary>
		internal int[] IdentityTags
		{ get { return this.m_identities; } }

		/// <summary>
		/// Return true if this abstract location represents at most one concrete memory location.
		/// </summary>
		internal abstract bool IsUniqueLocation();

		/// <summary>
		/// If we discover that this must be a unique location (in the call focus) use this to update the information as needed.
		/// </summary>
		internal abstract void UpdateAsUniqueLocation();

		/// <summary>
		/// Return the pointer targets that appear in the structure of this struct/class.
		/// </summary>
		internal APtrValueMap StructOrClassPointers
		{
			get
			{
				Contract.Requires(this.LocationType is IRStructType | this.LocationType is IRClassType);

				return this.m_ptrinfo;
			}
		}

		/// <summary>
		/// Compute the string of all the allocation/identity information for this location.
		/// </summary>
		protected string ComputeGeneralInfoString()
		{
			StringBuilder fmap = new StringBuilder();
			fmap.Append(this.GetAllocLocationStr());

			fmap.Append(", ID=[");
			for(int i = 0; i < this.m_identities.Length; ++i)
			{
				if(i != 0)
					fmap.Append(", ");
				fmap.Append(this.m_identities[i]);
			}
			fmap.Append("] ");

			fmap.Append("{");
			for(int i = 0; i < this.m_uml.Count; ++i)
			{
				if(i != 0)
					fmap.Append(", ");
				fmap.Append(this.m_uml[i].ToString());
			}
			fmap.Append("}");

			return fmap.ToString();
		}

		/// <summary>
		/// Mark that the contents at a given location were read and put the set of pointer targets that may be read in the list <c>into</c>.
		/// </summary>
		internal abstract Nullable<APtrValueMap> DoGeneralReadFromLocation(IRBasicAccessPath bap, int line);

		/// <summary>
		/// Mark that the contents at a given location were written and update the contents with the set of possible pointers in <c>from</c>.
		/// </summary>
		/// <param name="isuniquereftrgt">True if this write is to the unique possible target location</param>
		internal abstract void DoGeneralWriteToLocation(IRBasicAccessPath bap, APtrValueMap pvals, bool isuniquereftrgt, int line);

		/// <summary>
		/// Mark that the contents at a given location were written and update the contents with the single possible pointer in <c>pt</c>.
		/// </summary>
		/// <param name="isuniquereftrgt">True if this write is to the unique possible target location</param>
		internal void DoGeneralWriteToLocation_SingleTrgt(IRBasicAccessPath bap, APossiblePointerTarget pt, bool mustnonnull, bool isuniquereftrgt, int line)
		{
			APtrValueMap ppv = APtrValueMap.MakePtrWSingleTarget(pt, mustnonnull);
			this.DoGeneralWriteToLocation(bap, ppv, isuniquereftrgt, line);
		}

		/// <summary>
		/// Return an enumerator over the targets in this.
		/// </summary>
		internal abstract IEnumerator<ATrgtEnum> GetTargetEnum();

		/// <summary>
		/// Return the total number of possible pointer/reference targets from this
		/// </summary>
		internal abstract int TotalTargetCount();

		/// <summary>
		/// Given an <c>ATrgtEnum</c> return the <c>APossiblePointerTarget</c> associated with it.
		/// </summary>
		internal APossiblePointerTarget ResolvePtrFromEnum(ATrgtEnum tenum)
		{
			Contract.Requires(this.LocationType is IRPtrType | this.LocationType is IRClassType | this.LocationType is IRStructType);

			return this.m_ptrinfo.ResolvePtrFromEnum(tenum); 
		}
		
		/// <summary>
		/// Perform Sanity check on node state.
		/// </summary>
		internal virtual void CheckNodeOK(List<ALocation> memory)
		{
			if(this.m_identities.Length == 0 && this.m_escapelines.Length == 0)
				DebugReport.ReportError("Not Fresh and Not External??");

			if(!UniqueSortedArray.IsUniqueSortedArray<int>(this.m_identities))
				DebugReport.ReportError("Identities Not Sorted.");

			if(!UniqueSortedArray.IsUniqueSortedArray<EscapeSiteInfo>(this.m_escapelines))
				DebugReport.ReportError("Fresh Lines Not Sorted.");

			if(!UniqueSortedArray.IsUniqueSortedList<UseModInfo>(this.m_uml))
				DebugReport.ReportError("Use/Mod infor not sorted.");

			if(this.m_loctype is IRPtrType | this.m_loctype is IRClassType | this.m_loctype is IRStructType)
			{
				this.m_ptrinfo.CheckConsistent(this.m_loctype, !(this is AContainer) || ((AContainer)this).MustBeEmpty);

				if(!UniqueSortedArray.IsUniqueSortedArray<APossiblePointerTarget>(this.m_ptrinfo.PTargets))
					DebugReport.ReportError("Ptr info not sorted.");
			}

			//check use mod consistent
			for(int i = 0; i < this.m_uml.Count; ++i)
			{
				if(this.m_uml[i].UMAccessPath.IsEmptyAccessPath)
				{
					if(!(this.m_loctype is IRNumericType) && !(this.m_loctype is IREnumType) && !(this.m_loctype is IRReferenceType) && !(this.m_loctype is IRPtrType))
						DebugReport.ReportError("How did we write this??");
				}
				else
				{
					if((!(this.m_loctype is IRClassType) && !(this.m_loctype is IRStructType)) || !this.m_loctype.TypeDescription.AllTerminalAccessPaths.Contains(this.m_uml[i].UMAccessPath))
						DebugReport.ReportError("How did we write this??");
				}
			}

			////
			//check pointers
			HashSet<ATrgtEnum> checkset = new HashSet<ATrgtEnum>();
			IEnumerator<ATrgtEnum> trgtenum = this.GetTargetEnum();
			while(trgtenum.MoveNext())
			{
				ATrgtEnum tte = trgtenum.Current;

				if(tte.TrgtAddress < 0 || tte.TrgtAddress >= memory.Count)
					DebugReport.ReportError("We have an out of bounds edge reference.");

				ALocation tn = memory[tte.TrgtAddress];
				IRType tlt = tn.LocationType;

				if(tte.BAPPath == IRBasicAccessPath.EmptyAccessPath)
				{
					if(this.LocationType is IRReferenceType)
					{
						if(tlt is IRBoxedType)
						{
							if(!IRProgram.IsT1AssignableToT2(GlobalAnalysisState.TProg.TypeTable.LookupReferenceTypeFor(((IRBoxedType)tlt).BoxedTypeValue), this.LocationType))
								DebugReport.ReportError("The reference type stored to is not compatible with any of the types reported in the target node");
						}
						else
						{
							bool found = IRProgram.IsT1AssignableToT2(GlobalAnalysisState.TProg.TypeTable.LookupReferenceTypeFor(tlt), this.LocationType);
							foreach(IRBasicAccessPath oktbap in tlt.TypeDescription.AllTerminalAccessPaths)
							{
								if(!oktbap.IsEmptyAccessPath)
								{
									foreach(IRMemberField mf in oktbap.Path)
										found |= IRProgram.IsT1AssignableToT2(GlobalAnalysisState.TProg.TypeTable.LookupReferenceTypeFor(mf.FieldType), this.LocationType);
								}
							}

							if(!found)
								DebugReport.ReportError("The reference type stored to is not compatible with any of the storage locations in the target node");
						}
					}
					else
					{
						if(!IRProgram.IsT1AssignableToT2(GlobalAnalysisState.TProg.TypeTable.LookupPointerTypeFor((IRClassType)tlt), this.LocationType))
							DebugReport.ReportError("The pointer type stored to is not compatible with any of the types reported in the target node");
					}
				}
				else
				{
					IRMemberField sourceaccpf = tte.BAPPath.PathFirst;
					if(!this.m_loctype.TypeDescription.AllVisibleFields.Contains(sourceaccpf))
						DebugReport.ReportError("The source access field is not compatible with any of the types reported in the source node.");

					IRMemberField sourceaccpl = tte.BAPPath.PathLast;
					if(!(sourceaccpl.FieldType is IRPtrType))
						DebugReport.ReportError("The source access path ends with a non-pointer field but we have a pointer edge??");

					if(!IRProgram.IsT1AssignableToT2(GlobalAnalysisState.TProg.TypeTable.LookupPointerTypeFor((IRClassType)tlt), sourceaccpl.FieldType))
						DebugReport.ReportError("The pointer type stored to is not compatible with any of the types reported in the target node");
				}

				if((tn is AHomeLocation) && (!(this.LocationType is IRReferenceType)))
					DebugReport.ReportError("We have a pointer to a fixed location");

				if(checkset.Contains(tte))
					DebugReport.ReportError("Has multi edges with same offset!!!!");
				else
					checkset.Add(tte);
			}
		}

		/// <summary>
		/// True if two nodes are equal.
		/// </summary>
		internal static bool EqualALocation(ALocation l1, ALocation l2, bool ignoreids, bool explicitignoreescids)
		{
			if(l1.m_loctype != l2.m_loctype)
				return false;

			if(!ignoreids)
			{
				if(!UniqueSortedArray.IntArrayEquals(l1.m_identities, l2.m_identities))
					return false;
			}

			if(!explicitignoreescids)
			{
				if(!UniqueSortedArray.ArrayEquals<EscapeSiteInfo>(l1.m_escapelines, l2.m_escapelines))
					return false;
			}
			else
			{
				if((l1.m_escapelines.Length != 0) != (l2.m_escapelines.Length != 0))
					return false;
			}

			if(!UseModInfo.UseModListsEqual(l1.m_uml, l2.m_uml))
				return false;

			//
			//Target equality must be handled in the external graph isomorphism code
			//

			return l1.EqualALocationSubclass(l2);
		}

		/// <summary>
		/// Return true if the abstract locations are the same class and are equal (assuming we have already checked the info in the base <c>ALocation</c> class).
		/// </summary>
		protected abstract bool EqualALocationSubclass(ALocation ol);

		/// <summary>
		/// Delete any pointer or reference targets to locations in the delete set.
		/// </summary>
		internal abstract void DeletePossibleTargets(HashSet<int> deleteset);

		/// <summary>
		/// Copy the given location -- i.e., update the address based on the map <c>addrReMap</c>.
		/// </summary>
		internal abstract void MoveWithUpdatedAddress_ISOMapping(Dictionary<int, int> addrReMap);

		/// <summary>
		/// Copy the given location -- i.e., update the address based on the map <c>addrReMap</c>.
		/// We assume that this never creates new injective pointers (but preserves previously existing injectivity).
		/// </summary>
		internal abstract void MoveWithUpdatedAddresses_MayMergeButNoNewInj(Dictionary<int, int> addrReMap);

		/// <summary>
		/// Given two locations <c>l1</c> and <c>l2</c> from different source memory models compute the join of the information in them 
		/// returning a new ALocation with the given address.
		/// </summary>
		internal static ALocation JoinLocationsFromDifferentContexts(ALocation l1, ALocation l2)
		{
			Contract.Requires((l1 != null) & (l2 != null));
			Contract.Requires((l1 is AHomeLocation && l2 is AHomeLocation) || (l1 is AObject && l2 is AObject));
			Contract.Requires(l1.LocationType == l2.LocationType);
			Contract.Requires(l1.Address == l2.Address);
			Contract.Ensures(Contract.Result<ALocation>() != null);

			//common case
			int[] residentities;
			if((l1.m_identities == ALocation.EmptyIdentity) & (l2.m_identities == ALocation.EmptyIdentity))
				residentities = ALocation.EmptyIdentity;
			else
			{
				HashSet<int> ids = new HashSet<int>();
				ids.UnionWith(l1.m_identities);
				ids.UnionWith(l2.m_identities);

				residentities = new int[ids.Count];
				ids.CopyTo(residentities);
				Array.Sort(residentities);
			}

			//common case
			EscapeSiteInfo[] resfreshlocs;
			if((l1.m_escapelines == ALocation.EmptyAllocLines) & (l2.m_escapelines == ALocation.EmptyAllocLines))
				resfreshlocs = ALocation.EmptyAllocLines;
			else
			{
				HashSet<EscapeSiteInfo> fls = new HashSet<EscapeSiteInfo>();
				fls.UnionWith(l1.m_escapelines);
				fls.UnionWith(l2.m_escapelines);

				resfreshlocs = new EscapeSiteInfo[fls.Count];
				fls.CopyTo(resfreshlocs);
				Array.Sort(resfreshlocs);
			}

			List<UseModInfo> luml = new List<UseModInfo>(l1.m_uml);
			UseModInfo.UseModListsMerge(luml, l2.m_uml);

			return l1.JoinLocationsFromDifferentContextsSubclass(l2, residentities, resfreshlocs, luml);
		}

		/// <summary>
		/// Given a set of locations <c>lset</c> that are all from the same graph context compute a new summary locaion (with the given address) that over approximates them. 
		/// Update the addresses, targets, and injectivity as needed.
		/// </summary>
		internal static ALocation JoinLocationSetFromSameContext(List<ALocation> lset, int resaddress, Dictionary<int, int> addrReMap)
		{
			Contract.Requires(lset.Count > 1, "Why do this then?.");
			Contract.Ensures(Contract.Result<ALocation>() != null);

			HashSet<int> residentities = new HashSet<int>();
			HashSet<EscapeSiteInfo> resescapelines = new HashSet<EscapeSiteInfo>();
			List<UseModInfo> numl = new List<UseModInfo>();

			ALocation rcvrloc = null;
			foreach(ALocation ll in lset)
			{
				if(rcvrloc == null)
					rcvrloc = ll;

				residentities.UnionWith(ll.m_identities);
				resescapelines.UnionWith(ll.m_escapelines);
				UseModInfo.UseModListsMerge(numl, ll.m_uml);
			}

			int[] residentitiesa;
			if(residentities.Count == 0)
				residentitiesa = ALocation.EmptyIdentity;
			else
			{
				residentitiesa = new int[residentities.Count];
				residentities.CopyTo(residentitiesa);
				Array.Sort(residentitiesa);
			}

			EscapeSiteInfo[] resescapelinesa;
			if(resescapelines.Count == 0)
				resescapelinesa = ALocation.EmptyAllocLines;
			else
			{
				resescapelinesa = new EscapeSiteInfo[resescapelines.Count];
				resescapelines.CopyTo(resescapelinesa);
				Array.Sort(resescapelinesa);
			}

			return rcvrloc.JoinLocationSetFromSameContextSubclass(lset, resaddress, residentitiesa, resescapelinesa, numl, addrReMap);
		}

		/// <summary>
		/// Given this location and <c>ol</c> from a different source memory model finish the join of the subclass specific information in them 
		/// returning a new ALocation.
		/// </summary>
		protected abstract ALocation JoinLocationsFromDifferentContextsSubclass(ALocation ol, int[] residentities, EscapeSiteInfo[] resfreshlocs, List<UseModInfo> resuml);

		/// <summary>
		/// Given a set of locations <c>lset</c> that are all from the same graph context compute a new summary location (with the given address) that over approximates them. 
		/// Update the addresses, targets, and injectivity as needed.
		/// </summary>
		protected abstract ALocation JoinLocationSetFromSameContextSubclass(List<ALocation> lset, int resaddress, int[] residentities, EscapeSiteInfo[] resfreshlocs, List<UseModInfo> resuml, Dictionary<int, int> addrReMap);

		///////////////////////////////////////////////////////////////////
		//Support for method call and flow operations

		/// <summary>
		/// At a loop header update the allocation flow and usemod flow info to the "previous" loop iteration values.
		/// </summary>
		internal void AdvanceUseModAndAllocInfoToLoopPrevious(int loopminpos, int loopmaxpos, int loopprevpos)
		{
			///////
			//esc first, check if we need to do anything first then do all the rewriting if needed
			bool hasescflow = false;
			for(int i = 0; i < this.m_escapelines.Length; ++i)
				hasescflow |= ((loopminpos <= this.m_escapelines[i].EscapeLine) & (this.m_escapelines[i].EscapeLine <= loopmaxpos));

			if(hasescflow)
			{
				HashSet<EscapeSiteInfo> esciset = new HashSet<EscapeSiteInfo>();
				for(int i = 0; i < this.m_escapelines.Length; ++i)
					esciset.Add(this.m_escapelines[i].UpdateLoopFlowLineAsNeeded(loopminpos, loopmaxpos, loopprevpos));

				this.m_escapelines = new EscapeSiteInfo[esciset.Count];
				esciset.CopyTo(this.m_escapelines);
				Array.Sort(this.m_escapelines);
			}
		}

		/// <summary>
		/// Advance the loop index info
		/// </summary>
		internal void AdvanceLoopPositionInfo()
		{
			if(this.m_loctype is IRPtrType | this.m_loctype is IRStructType | this.m_loctype is IRClassType)
			{
				if(this.m_ptrinfo.HasLoopInfo())
					this.m_ptrinfo = this.m_ptrinfo.AdvanceLoopPositionInfo();
			}
		}

		/// <summary>
		/// Clear all loop index info
		/// </summary>
		internal void ClearLoopPositionInfo()
		{
			if(this.m_loctype is IRPtrType | this.m_loctype is IRStructType | this.m_loctype is IRClassType)
			{
				if(this.m_ptrinfo.HasLoopInfo())
					this.m_ptrinfo = this.m_ptrinfo.ClearLoopPositionInfo();
			}
		}

		/// <summary>
		/// Return true if any pointer fields have been modified inside loopmin/loopmax with an optional 
		/// paramter to specify a specific field (set onlyptr to true if we don't want to check for scalar mods as well).
		/// </summary>
		internal bool HasLocationBeenModInLoopWOptScalarCheck(int loopmin, int loopmax, bool onlyptr, int okmodposition)
		{
			for(int i = 0; i < this.m_uml.Count; ++i)
			{
				UseModInfo umi = this.m_uml[i];
				bool pathmatch;
				if(umi.UMAccessPath.IsEmptyAccessPath)
				{
					IRType loctype = this.m_loctype;
					pathmatch = ((loctype is IRPtrType) || (loctype is IRReferenceType) || !onlyptr);
				}
				else
				{
					IRType baptype = umi.UMAccessPath.PathLast.FieldType;
					pathmatch = ((baptype is IRPtrType) || (baptype is IRReferenceType) || !onlyptr);
				}

				bool inrng = ((okmodposition != umi.UMLine) & ((loopmin <= umi.UMLine) & (umi.UMLine <= loopmax)));
				bool ismod = (umi.UMTag == UseModInfo.Value.ModAsInternal) | (umi.UMTag == UseModInfo.Value.ModAsExternal);
				if(pathmatch & inrng & ismod)
					return true;
			}

			return false;
		}

		/// <summary>
		/// Take a map from addresses to cutpoint ids that are going to be associated with them and do the needed replacements.
		/// </summary>
		internal abstract void ReplaceAssociatedCutpointsAsNeeded(Dictionary<int, int> addressToCPIDMap, HashSet<int> idsofinterest);

		/// <summary>
		/// Take a map from the cutpoint id (which is a negative number as the pointer/reference address) and replace and poitners/references 
		/// that have possible targets with the new values.
		/// </summary>
		internal abstract void UpdateOutEdgesAfterCutpointEdgeExpand(Dictionary<int, int[]> cpidToAddressMap, HashSet<int> idsofinterest);

		/// <summary>
		/// Give this node a unique identity. 
		/// </summary>
		internal void ProcessRegionInfoForMoveIntoCalleeScope(Dictionary<int, Tuple<List<UseModInfo>, int[], EscapeSiteInfo[]>> callstatemappings)
		{
			int newid = GenNextIdentityTag();
			int[] exptags = this.m_identities;
			EscapeSiteInfo[] freshlines = this.m_escapelines;
			callstatemappings.Add(newid, new Tuple<List<UseModInfo>, int[], EscapeSiteInfo[]>(this.m_uml, exptags, freshlines));

			////
			//now that we have saved the information that we will restore with at call return we clear the values as needed for the callee scope
			this.m_identities = new int[] { newid };
			this.m_escapelines = ALocation.EmptyAllocLines;
			this.m_uml = new List<UseModInfo>();
		}

		/// <summary>
		/// Support for remapping id sets on retrn, expand the id values based on the map
		/// </summary>
		internal void RestoreCallerScopeIdsExtendEscapeInfo(Dictionary<int, Tuple<List<UseModInfo>, int[], EscapeSiteInfo[]>> callstatemappings, int line)
		{
			HashSet<int> ttr = new HashSet<int>();
			HashSet<UseModInfo> umv = new HashSet<UseModInfo>();
			HashSet<EscapeSiteInfo> ffr = new HashSet<EscapeSiteInfo>();

			for(int i = 0; i < this.m_identities.Length; ++i)
			{
				int iid = this.m_identities[i];
				Tuple<List<UseModInfo>, int[], EscapeSiteInfo[]> info = callstatemappings[iid];

				umv.UnionWith(info.Item1);
				ttr.UnionWith(info.Item2);
				ffr.UnionWith(info.Item3);
			}

			int[] newids;
			if(ttr.Count == 0)
				newids = ALocation.EmptyIdentity;
			else
			{
				newids = new int[ttr.Count];
				ttr.CopyTo(newids);
				Array.Sort(newids);
			}

			this.ResetInfoAtCallExit(umv, newids, ffr, line);
		}

		/// <summary>
		/// Take the summary of all the external information that we eliminated during the call into the callee body, 
		/// restore/merge it with any effects of the callee as needed.
		/// </summary>
		private void ResetInfoAtCallExit(HashSet<UseModInfo> externalusemod, int[] remapids, HashSet<EscapeSiteInfo> frshlines, int line)
		{
			this.m_identities = remapids;

			HashSet<EscapeSiteInfo> resesc = new HashSet<EscapeSiteInfo>(frshlines);
			for(int i = 0; i < this.m_escapelines.Length; ++i)
				resesc.Add(new EscapeSiteInfo(line, line, this.m_escapelines[i].EscapeIdentity));

			if(resesc.Count == 0)
				this.m_escapelines = ALocation.EmptyAllocLines;
			else
			{
				this.m_escapelines = new EscapeSiteInfo[resesc.Count];
				resesc.CopyTo(this.m_escapelines);
				Array.Sort(this.m_escapelines);
			}
			
			HashSet<UseModInfo> resumi = new HashSet<UseModInfo>(externalusemod);
			for(int i = 0; i < this.m_uml.Count; ++i)
			{
				UseModInfo umi = this.m_uml[i];

				if((umi.UMTag == UseModInfo.Value.UseAsInternal) | (umi.UMTag == UseModInfo.Value.ModAsInternal))
					resumi.Add(new UseModInfo(umi.UMTag, line, umi.UMAccessPath));
				else
				{
					if(this.MustBeInternalAlloc())
					{
						if(umi.UMTag == UseModInfo.Value.UseAsExternal)
							resumi.Add(new UseModInfo(UseModInfo.Value.UseAsInternal, line, umi.UMAccessPath));
						else
							resumi.Add(new UseModInfo(UseModInfo.Value.ModAsInternal, line, umi.UMAccessPath));
					}
					else
					{
						resumi.Add(new UseModInfo(umi.UMTag, line, umi.UMAccessPath));

						if(this.MayBeInternalAlloc())
						{
							if(umi.UMTag == UseModInfo.Value.UseAsExternal)
								resumi.Add(new UseModInfo(UseModInfo.Value.UseAsInternal, line, umi.UMAccessPath));
							else
								resumi.Add(new UseModInfo(UseModInfo.Value.ModAsInternal, line, umi.UMAccessPath));
						}
					}
				}
			}

			//remember cuml is an alias for the usemod list in this
			this.m_uml.Clear();
			this.m_uml.AddRange(resumi);
			this.m_uml.Sort();
		}

		/// <summary>
		/// Support for remapping id sets on memoized call values.
		/// </summary>
		internal void RemapIdentityTags(Dictionary<int, HashSet<int>> idtm)
		{
			if(this.m_identities != ALocation.EmptyIdentity)
			{
				HashSet<int> nidts = new HashSet<int>();
				for(int i = 0; i < this.m_identities.Length; ++i)
					nidts.UnionWith(idtm[this.m_identities[i]]);

				if(nidts.Count == 0)
					this.m_identities = ALocation.EmptyIdentity;
				else
				{
					this.m_identities = new int[nidts.Count];
					nidts.CopyTo(this.m_identities);
					Array.Sort(this.m_identities);
				}
			}
		}

		/// <summary>
		/// Uniqueify the idset for this to an arbitrary fresh id.
		/// </summary>
		internal void UniqueifyIdentityTags()
		{ this.m_identities = new int[] { GenNextIdentityTag() }; }

		/// <summary>
		/// Replace the current id set with a simplified one as given by the set during the computation of the summary input/output 
		/// models in the final info computations.
		/// </summary>
		internal void SetIDTagsForFinalProcessInfo(int[] simpleids)
		{ this.m_identities = simpleids; }
	}
}
